맨위로가기

버퍼 오버플로

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

버퍼 오버플로는 할당된 메모리 공간을 초과하여 데이터를 쓰는 오류로, 프로그램의 보안 취약점을 야기한다. 이는 불충분한 경계 검사로 인해 발생하며, 문자열 복사 시 흔히 나타난다. 버퍼 오버플로를 악용하면 프로그램의 실행 흐름을 변경하거나, 시스템의 권한을 탈취하는 등의 공격이 가능하다. 스택 기반, 힙 기반 등 다양한 방식으로 악용될 수 있으며, NOP 슬라이드, 레지스터로 건너뛰기 등의 기술이 사용된다.

C/C++는 버퍼 오버플로에 취약하며, 안전한 프로그래밍 언어 선택, 안전한 라이브러리 사용, 버퍼 오버플로 보호 기법, 실행 공간 보호, 주소 공간 배치 난수화 등의 대응 수단이 존재한다. 심층 패킷 조사를 통해 공격 시도를 탐지할 수도 있다. 버퍼 오버플로 개념은 1972년에 처음 소개되었으며, 모리스 웜, 코드 레드 웜, SQL 슬래머 웜 등 여러 악성 코드에서 악용되었다.

더 읽어볼만한 페이지

  • 소프트웨어 버그 - 교착 상태
    교착 상태는 둘 이상의 프로세스가 자원을 점유하고 서로의 자원을 요청하여 더 이상 진행할 수 없는 상태를 의미하며, 상호 배제, 점유 대기, 비선점, 순환 대기 네 가지 조건이 모두 충족되어야 발생하고, 운영 체제는 이를 예방, 회피, 무시, 발견하는 방법으로 관리한다.
  • 소프트웨어 버그 - 글리치
    글리치는 예기치 않은 오작동이나 오류를 뜻하며, 전자 공학, 컴퓨터, 비디오 게임, 텔레비전 방송, 대중문화 등 다양한 분야에서 기능 실패, 오류, 그래픽 및 사운드 문제, 신호 오류 등의 이상 현상을 포괄적으로 지칭하는 용어이다.
  • 취약점 공격 - 보안 취약점
    보안 취약점은 시스템의 설계, 구현, 운영, 관리상 결함이나 약점으로, 위협에 의해 악용되어 시스템 보안 정책을 위반할 수 있는 요소이며, ISO 27005, IETF RFC 4949, NIST SP 800-30, ENISA 등 다양한 기관에서 정의하고 있다.
  • 취약점 공격 - 인터넷 보안
    인터넷 보안은 사이버 위협, 악성 소프트웨어, 서비스 거부 공격 등으로부터 정보와 시스템을 보호하기 위해 네트워크 계층 보안, 다단계 인증, 방화벽 등 다양한 기술과 방법을 포괄한다.
  • 컴퓨터 메모리 - 플래시 메모리
    플래시 메모리는 전기적으로 데이터의 쓰기 및 삭제가 가능한 비휘발성 메모리 기술로, 마스오카 후지오 박사가 발명하여 카메라 플래시와 유사한 소거 방식으로 인해 명명되었으며, NOR형과 NAND형으로 나뉘어 각기 다른 분야에 적용된다.
  • 컴퓨터 메모리 - 메모리 계층 구조
    메모리 계층 구조는 CPU 데이터 접근 속도 향상을 위해 레지스터, 캐시, RAM, 보조 기억 장치 등으로 구성되며, 속도, 용량, 비용이 다른 계층들을 통해 효율적인 메모리 관리를 가능하게 한다.
버퍼 오버플로
보안 취약점
종류소프트웨어 보안 취약점
발견 시기1972년
악용 방법서비스 거부 공격
임의 코드 실행
권한 상승
일반적인 약점 열거CWE-119: 버퍼에 할당된 공간 내에서 작업 제한의 부적절한 제한 ('고전적인 버퍼 오버플로')
Common Vulnerabilities and Exposures (CVE)CVE-2019-14287
CVE-2019-14288
CVE-2019-14899
CVE-2019-14900
CVE-2019-14901
CVE-2019-15845
CVE-2019-15903
CVE-2019-16276
CVE-2019-16277
CVE-2019-16719
CVE-2019-17006
CVE-2019-17007
CVE-2019-17008
CVE-2019-17009
CVE-2019-17010
CVE-2019-17011
CVE-2019-17012
CVE-2019-17013
CVE-2019-17014
CVE-2019-17015
CVE-2019-17016
CVE-2019-17017
CVE-2019-17018
CVE-2019-17019
CVE-2019-17020
CVE-2019-17021
CVE-2019-17022
CVE-2019-17023
CVE-2019-17024
CVE-2019-17025
CVE-2019-17026
CVE-2019-17027
CVE-2019-17028
CVE-2019-17029
CVE-2019-17030
CVE-2019-17031
CVE-2019-17032
CVE-2019-17033
CVE-2019-17034
CVE-2019-17035
CVE-2019-17036
CVE-2019-17037
CVE-2019-17038
CVE-2019-17039
CVE-2019-17040
CVE-2019-17041
CVE-2019-17042
CVE-2019-17043
CVE-2019-17044
CVE-2019-17045
CVE-2019-17046
CVE-2019-17047
CVE-2019-17048
CVE-2019-17049
CVE-2019-17050
CVE-2019-17051
CVE-2019-17052
CVE-2019-17053
CVE-2019-17054
CVE-2019-17055
CVE-2019-17056
CVE-2019-17057
CVE-2019-17058
CVE-2019-17059
CVE-2019-17060
CVE-2019-17061
CVE-2019-17062
CVE-2019-17063
CVE-2019-17064
CVE-2019-17065
CVE-2019-17066
CVE-2019-17067
CVE-2019-17068
CVE-2019-17069
CVE-2019-17070
CVE-2019-17071
CVE-2019-17072
CVE-2019-17073
CVE-2019-17074
CVE-2019-17075
CVE-2019-17076
CVE-2019-17077
CVE-2019-17078
CVE-2019-17079
CVE-2019-17080
CVE-2019-17081
CVE-2019-17082
CVE-2019-17083
CVE-2019-17084
CVE-2019-17085
CVE-2019-17086
CVE-2019-17087
CVE-2019-17088
CVE-2019-17089
CVE-2019-17090
CVE-2019-17091
CVE-2019-17092
CVE-2019-17093
CVE-2019-17094
CVE-2019-17095
CVE-2019-17096
CVE-2019-17097
CVE-2019-17098
CVE-2019-17099
CVE-2019-17100
CVE-2019-17101
CVE-2019-17102
CVE-2019-17103
CVE-2019-17104
CVE-2019-17105
CVE-2019-17106
CVE-2019-17107
CVE-2019-17108
CVE-2019-17109
CVE-2019-17110
CVE-2019-17111
CVE-2019-17112
CVE-2019-17113
CVE-2019-17114
CVE-2019-17115
CVE-2019-17116
CVE-2019-17117
CVE-2019-17118
CVE-2019-17119
CVE-2019-17120
CVE-2019-17121
CVE-2019-17122
CVE-2019-17123
CVE-2019-17124
CVE-2019-17125
CVE-2019-17126
CVE-2019-17127
CVE-2019-17128
CVE-2019-17129
CVE-2019-17130
CVE-2019-17131
CVE-2019-17132
CVE-2019-17133
CVE-2019-17134
CVE-2019-17135
CVE-2019-17136
CVE-2019-17137
CVE-2019-17138
CVE-2019-17139
CVE-2019-17140
CVE-2019-17141
CVE-2019-17142
CVE-2019-17143
CVE-2019-17144
CVE-2019-17145
CVE-2019-17146
CVE-2019-17147
CVE-2019-17148
CVE-2019-17149
CVE-2019-17150
CVE-2019-17151
CVE-2019-17152
CVE-2019-17153
CVE-2019-17154
CVE-2019-17155
CVE-2019-17156
CVE-2019-17157
CVE-2019-17158
CVE-2019-17159
CVE-2019-17160
CVE-2019-17161
CVE-2019-17162
CVE-2019-17163
CVE-2019-17164
CVE-2019-17165
CVE-2019-17166
CVE-2019-17167
CVE-2019-17168
CVE-2019-17169
CVE-2019-17170
CVE-2019-17171
CVE-2019-17172
CVE-2019-17173
CVE-2019-17174
CVE-2019-17175
CVE-2019-17176
CVE-2019-17177
CVE-2019-17178
CVE-2019-17179
CVE-2019-17180
CVE-2019-17181
CVE-2019-17182
CVE-2019-17183
CVE-2019-17184
CVE-2019-17185
CVE-2019-17186
CVE-2019-17187
CVE-2019-17188
CVE-2019-17189
CVE-2019-17190
CVE-2019-17191
CVE-2019-17192
CVE-2019-17193
CVE-2019-17194
CVE-2019-17195
CVE-2019-17196
CVE-2019-17197
CVE-2019-17198
CVE-2019-17199
CVE-2019-17200
CVE-2019-17201
CVE-2019-17202
CVE-2019-17203
CVE-2019-17204
CVE-2019-17205
CVE-2019-17206
CVE-2019-17207
CVE-2019-17208
CVE-2019-17209
CVE-2019-17210
CVE-2019-17211
CVE-2019-17212
CVE-2019-17213
CVE-2019-17214
CVE-2019-17215
CVE-2019-17216
CVE-2019-17217
CVE-2019-17218
CVE-2019-17219
CVE-2019-17220
CVE-2019-17221
CVE-2019-17222
CVE-2019-17223
CVE-2019-17224
CVE-2019-17225
CVE-2019-17226
CVE-2019-17227
CVE-2019-17228
CVE-2019-17229
CVE-2019-17230
CVE-2019-17231
CVE-2019-17232
CVE-2019-17233
CVE-2019-17234
CVE-2019-17235
CVE-2019-17236
CVE-2019-17237
CVE-2019-17238
CVE-2019-17239
CVE-2019-17240
CVE-2019-17241
CVE-2019-17242
CVE-2019-17243
CVE-2019-17244
CVE-2019-17245
CVE-2019-17246
CVE-2019-17247
CVE-2019-17248
CVE-2019-17249
CVE-2019-17250
CVE-2019-17251
CVE-2019-17252
CVE-2019-17253
CVE-2019-17254
CVE-2019-17255
CVE-2019-17256
CVE-2019-17257
CVE-2019-17258
CVE-2019-17259
CVE-2019-17260
CVE-2019-17261
CVE-2019-17262
CVE-2019-17263
CVE-2019-17264
CVE-2019-17265
CVE-2019-17266
CVE-2019-17267
CVE-2019-17268
CVE-2019-17269
CVE-2019-17270
CVE-2019-17271
CVE-2019-17272
CVE-2019-17273
CVE-2019-17274
CVE-2019-17275
CVE-2019-17276
CVE-2019-17277
CVE-2019-17278
CVE-2019-17279
CVE-2019-17280
CVE-2019-17281
CVE-2019-17282
CVE-2019-17283
CVE-2019-17284
CVE-2019-17285
CVE-2019-17286
CVE-2019-17287
CVE-2019-17288
CVE-2019-17289
CVE-2019-17290
CVE-2019-17291
CVE-2019-17292
CVE-2019-17293
CVE-2019-17294
CVE-2019-17295
CVE-2019-17296
CVE-2019-17297
CVE-2019-17298
CVE-2019-17299
CVE-2019-17300
CVE-2019-17301
CVE-2019-17302
CVE-2019-17303
CVE-2019-17304
CVE-2019-17305
CVE-2019-17306
CVE-2019-17307
CVE-2019-17308
CVE-2019-17309
CVE-2019-17310
CVE-2019-17311
CVE-2019-17312
CVE-2019-17313
CVE-2019-17314
CVE-2019-17315
CVE-2019-17316
CVE-2019-17317
CVE-2019-17318
CVE-2019-17319
CVE-2019-17320
CVE-2019-17321
CVE-2019-17322
CVE-2019-17323
CVE-2019-17324
CVE-2019-17325
CVE-2019-17326
CVE-2019-17327
CVE-2019-17328
CVE-2019-17329
CVE-2019-17330
CVE-2019-17331
CVE-2019-17332
CVE-2019-17333
CVE-2019-17334
CVE-2019-17335
CVE-2019-17336
CVE-2019-17337
CVE-2019-17338
CVE-2019-17339
CVE-2019-17340
CVE-2019-17341
CVE-2019-17342
CVE-2019-17343
CVE-2019-17344
CVE-2019-17345
CVE-2019-17346
CVE-2019-17347
CVE-2019-17348
CVE-2019-17349
CVE-2019-17350
CVE-2019-17351
CVE-2019-17352
CVE-2019-17353
CVE-2019-17354
CVE-2019-17355
CVE-2019-17356
CVE-2019-17357
CVE-2019-17358
CVE-2019-17359
CVE-2019-17360
CVE-2019-17361
CVE-2019-17362
CVE-2019-17363
CVE-2019-17364
CVE-2019-17365
CVE-2019-17366
CVE-2019-17367
CVE-2019-17368
CVE-2019-17369
CVE-2019-17370
CVE-2019-17371
CVE-2019-17372
CVE-2019-17373
CVE-2019-17374
CVE-2019-17375
CVE-2019-17376
CVE-2019-17377
CVE-2019-17378
CVE-2019-17379
CVE-2019-17380
CVE-2019-17381
CVE-2019-17382
CVE-2019-17383
CVE-2019-17384
CVE-2019-17385
CVE-2019-17386
CVE-2019-17387
CVE-2019-17388
CVE-2019-17389
CVE-2019-17390
CVE-2019-17391
CVE-2019-17392
CVE-2019-17393
CVE-2019-17394
CVE-2019-17395
CVE-2019-17396
CVE-2019-17397
CVE-2019-17398
CVE-2019-17399
CVE-2019-17400
CVE-2019-17401
CVE-2019-17402
CVE-2019-17403
CVE-2019-17404
CVE-2019-17405
CVE-2019-17406
CVE-2019-17407
CVE-2019-17408
CVE-2019-17409
CVE-2019-17410
CVE-2019-17411
CVE-2019-17412
CVE-2019-17413
CVE-2019-17414
CVE-2019-17415
CVE-2019-17416
CVE-2019-17417
CVE-2019-17418
CVE-2019-17419
CVE-2019-17420
CVE-2019-17421
CVE-2019-17422
CVE-2019-17423
CVE-2019-17424
CVE-2019-17425
CVE-2019-17426
CVE-2019-17427
CVE-2019-17428
CVE-2019-17429
CVE-2019-17430
CVE-2019-17431
CVE-2019-17432
CVE-2019-17433
CVE-2019-17434
CVE-2019-17435
CVE-2019-17436
CVE-2019-17437
CVE-2019-17438
CVE-2019-17439
CVE-2019-17440
CVE-2019-17441
CVE-2019-17442
CVE-2019-17443
CVE-2019-17444
CVE-2019-17445
CVE-2019-17446
CVE-2019-17447
CVE-2019-17448
CVE-2019-17449
CVE-2019-17450
CVE-2019-17451
CVE-2019-17452
CVE-2019-17453
CVE-2019-17454
CVE-2019-17455
CVE-2019-17456
CVE-2019-17457
CVE-2019-17458
CVE-2019-17459
CVE-2019-17460
CVE-2019-17461
CVE-2019-17462
CVE-2019-17463
CVE-2019-17464
CVE-2019-17465
CVE-2019-17466
CVE-2019-17467
CVE-2019-17468
CVE-2019-17469
CVE-2019-17470
CVE-2019-17471
CVE-2019-17472
CVE-2019-17473
CVE-2019-17474
CVE-2019-17475
CVE-2019-17476
CVE-2019-17477
CVE-2019-17478
CVE-2019-17479
CVE-2019-17480
CVE-2019-17481
CVE-2019-17482
CVE-2019-17483
CVE-2019-17484
CVE-2019-17485
CVE-2019-17486
CVE-2019-17487
CVE-2019-17488
CVE-2019-17489
CVE-2019-17490
CVE-2019-17491
CVE-2019-17492
CVE-2019-17493
CVE-2019-17494
CVE-2019-17495
CVE-2019-17496
CVE-2019-17497
CVE-2019-17498
CVE-2019-17499
CVE-2019-17500
CVE-2019-17501
CVE-2019-17502
CVE-2019-17503
CVE-2019-17504
CVE-2019-17505
CVE-2019-17506
CVE-2019-17507
CVE-2019-17508
CVE-2019-17509
CVE-2019-17510
CVE-2019-17511
CVE-2019-17512
CVE-2019-17513
CVE-2019-17514
CVE-2019-17515
CVE-2019-17516
CVE-2019-17517
CVE-2019-17518
CVE-2019-17519
CVE-2019-17520
CVE-2019-17521
CVE-2019-17522
CVE-2019-17523
CVE-2019-17524
CVE-2019-17525
CVE-2019-17526
CVE-2019-17527
CVE-2019-17528
CVE-2019-17529
CVE-2019-17530
CVE-20

2. 기술적인 설명

버퍼 오버플로는 불충분한 경계 검사에 의해 버퍼에 쓰인 데이터가 버퍼에 이미 할당된 근접한 메모리 주소에 있는 데이터 값을 오염시킬 때 발생한다. 대부분 이는 문자열을 하나의 버퍼에서 다른 버퍼로 복사할 때 발생한다.

컴퓨터 프로그램을 만들 때, 고정 길이의 버퍼라고 불리는 영역을 확보하여 거기에 데이터를 저장하는 기법이 자주 사용된다.

예를 들어, 이메일주소는 200자를 넘지 않을 것이라고 예상하고

# 200자 분량의 영역을 버퍼로 준비한다.

# 사용자가 200자보다 긴 이메일 주소를 입력한다.

# 프로그램이 버퍼의 크기를 확인하지 않고 입력 데이터를 덮어쓴다.

# 버퍼로 확보한 영역을 벗어나 데이터가 덮어씌워진다.

이것이 버퍼 오버플로이다. 가령 덮어씌워진 부분에 프로그램의 동작상 의미가 있는 데이터가 있다면, 이를 덮어쓰고 파괴함으로써 프로그램은 사용자의 의도와 다른 동작을 보일 것이다.

이처럼 버퍼 오버플로는, 프로그램이 준비한 버퍼의 크기를 초과하여 데이터를 덮어쓰는 버그이다.

==== 기본 예제 ====

메모리 상에 인접한 8바이트 문자열 버퍼 A와 2바이트 정수 B가 있을 때, A의 크기를 초과하는 문자열을 A에 복사하면 B의 값이 의도치 않게 변경될 수 있다. 예를 들어, "excessive"와 같이 A의 크기보다 큰 문자열을 복사하면 B를 덮어쓰게 된다.

아래는 C 언어로 작성된 예제이다. 프로그램은 메모리에서 인접한 두 개의 변수를 갖는다. 하나는 8바이트 길이의 문자열 버퍼 A이고, 다른 하나는 2바이트 빅 엔디안 정수 B이다.

```text

char A[8] = "";

unsigned short B = 1979;

```

처음 A는 0바이트만 포함하고, B는 숫자 1979를 포함한다.

변수 이름AB
1979
16진수 값000000000000000007BB



프로그램이 A 버퍼에 널 종료 문자열 "excessive"를 ASCII 인코딩으로 저장하려고 시도할때, "excessive"는 9글자 길이이며 널 종료 문자를 포함하여 10바이트로 인코딩되지만 A는 8바이트만 사용할 수 있다. 문자열의 길이를 확인하지 못하여 B의 값도 덮어씁니다.

```text

strcpy(A, "excessive");

```

변수 이름AB
excessiv25856
16진수65786365737369766500



B의 값은 의도치 않게 문자열의 일부로 형성된 숫자로 대체되었다. 이 예에서 "e" 다음에 0 바이트가 오면 25856이 된다.

할당된 메모리의 끝을 지나 데이터를 쓰는 것은 때때로 운영 체제에 의해 감지되어 프로세스를 종료하는 세그먼테이션 오류 오류를 생성할 수 있다.

이러한 버퍼 오버플로를 방지하기 위해, `strcpy` 대신 `strlcpy` 함수를 사용하여 최대 용량을 제한할 수 있다.

```text

strlcpy(A, "excessive", sizeof(A));

```

`strlcpy`는 `strncpy`보다 안전하지만, 소스 문자열의 길이가 버퍼 크기보다 크거나 같은 경우 대상 버퍼를 널로 종료하지 않을 수 있으므로 주의해야 한다.

2. 1. 기본 예제

메모리 상에 인접한 8바이트 문자열 버퍼 A와 2바이트 정수 B가 있을 때, A의 크기를 초과하는 문자열을 A에 복사하면 B의 값이 의도치 않게 변경될 수 있다. 예를 들어, "excessive"와 같이 A의 크기보다 큰 문자열을 복사하면 B를 덮어쓰게 된다.

아래는 C 언어로 작성된 예제이다. 프로그램은 메모리에서 인접한 두 개의 변수를 갖는다. 하나는 8바이트 길이의 문자열 버퍼 A이고, 다른 하나는 2바이트 빅 엔디안 정수 B이다.

```text

char A[8] = "";

unsigned short B = 1979;

```

처음 A는 0바이트만 포함하고, B는 숫자 1979를 포함한다.

변수 이름AB
[널 문자열]1979
16진수 값000000000000000007BB



프로그램이 A 버퍼에 널 종료 문자열 "excessive"를 ASCII 인코딩으로 저장하려고 시도할때, "excessive"는 9글자 길이이며 널 종료 문자를 포함하여 10바이트로 인코딩되지만 A는 8바이트만 사용할 수 있다. 문자열의 길이를 확인하지 못하여 B의 값도 덮어씁니다.

```text

strcpy(A, "excessive");

```

변수 이름AB
excessiv25856
16진수65786365737369766500



B의 값은 의도치 않게 문자열의 일부로 형성된 숫자로 대체되었다. 이 예에서 "e" 다음에 0 바이트가 오면 25856이 된다.

할당된 메모리의 끝을 지나 데이터를 쓰는 것은 때때로 운영 체제에 의해 감지되어 프로세스를 종료하는 세그먼테이션 오류 오류를 생성할 수 있다.

이러한 버퍼 오버플로를 방지하기 위해, `strcpy` 대신 `strlcpy` 함수를 사용하여 최대 용량을 제한할 수 있다.

```text

strlcpy(A, "excessive", sizeof(A));

```

`strlcpy`는 `strncpy`보다 안전하지만, 소스 문자열의 길이가 버퍼 크기보다 크거나 같은 경우 대상 버퍼를 널로 종료하지 않을 수 있으므로 주의해야 한다.

3. 이용

버퍼 오버플로 취약점을 악용하는 기술은 아키텍처, 운영 체제, 그리고 메모리 영역에 따라 다르다. 예를 들어, 힙(동적으로 할당된 메모리에 사용됨)에서의 악용은 콜 스택에서의 악용과 뚜렷하게 다릅니다. 일반적으로 힙 악용은 대상 시스템에서 사용되는 힙 관리자에 따라 다르며, 스택 악용은 아키텍처 및 컴파일러에서 사용되는 호출 규칙에 따라 달라집니다.

3. 1. 스택 기반 이용

기술적 성향을 가진 악의적 사용자가 스택 기반 버퍼 오버플로를 이용해 프로그램을 조작하는 방법은 다음과 같다:

  • 스택에 가까운 지역 변수를 덮어씀으로써 프로그램의 동작을 공격자에게 유리하게 바꾼다.
  • 스택 프레임 안의 귀환 주소를 덮어쓴다. 함수가 귀환하면 실행이 공격자가 지정한 귀환 주소로부터 재개되는데, 보통 사용자 입력으로 채워진 버퍼이다.
  • 함수 포인터 또는 예외 핸들러를 덮어씀으로써 이후에 실행되는 부분을 가로챈다.


트램폴라이닝이라는 방법을 사용하면, 사용자가 지정한 데이터의 주소를 모르더라도, 레지스터에 그 주소가 저장되어 있다면, 실행 흐름을 사용자 제공 데이터로 도약하도록 하는 어떤 실행코드의 주소로 귀환 주소를 덮어쓸 수 있다. 위치가 레지스터 R에 저장되어 있다면 jump R이나 call R과 같이 R에 저장되어 있는 주소로 건너뛰는 작동코드 opcode로 사용자 제공 데이터가 실행되도록 할 수 있다. 적당한 작동코드 또는 바이트의 위치는 DLL이나 실행 파일 자체 안에서 찾을 수 있다. 그러나 작동 코드의 주소는 전형적으로는 어떠한 무효 문자도 포함할 수 없고, 이러한 작동 코드의 위치는 응용 프로그램마다, 운영 체제 버전에 따라 달라질 수 있다. 메타스플로잇 프로젝트는 그러한 적당한 작동코드의 데이터베이스로 윈도우 운영 체제에 관한 것이 나열되어 있다.

스택 기반 버퍼 오버플로는 "덮어쓰기될 버퍼가 스택(즉, 지역 변수 또는 드물게 함수의 매개변수)에 할당"될 수 있는 경우 발생하는 취약점이다.[42] 이러한 취약점은 퍼징을 사용하여 발견되는 경우가 많다.[49]

C 언어나 C++로 작성된 프로그램의 경우, 사용자 공간을 더 세분화한다. 이들 언어로 작성된 프로그램에서는 가상 주소의 최저위부터 순서대로 프로그램의 실행 코드를 배치하는 '''코드 영역 (텍스트 영역이라고도 함)''', 초기화된 정적 변수, 전역 변수를 배치하는 '''데이터 영역''', 초기화되지 않은 정적 변수, 전역 변수를 배치하는 '''bss 영역'''을 확보한다.

한편, 사용자 공간의 가상 주소 최고위는 함수의 호출 스택을 저장하는 (가변 크기의) '''스택 영역'''으로 사용된다.

최저위최고위
코드 영역정적 영역힙 영역스택 영역
데이터 영역bss 영역



스택 영역은 프로세스 내에서 호출되는 함수의 호출 스택을 저장하는 영역으로, 호출 스택 내의 각 함수 데이터 ('''스택 프레임'''이라고 함)를 나란히 저장한다.프로세스 내에서 함수 f가 함수 g를 호출한 경우, 호출 스택은 다음과 같다:

최저 주소최고 주소
g의 스택 프레임f의 스택 프레임
g의 처리에 필요한 임시 정보g의 지역 변수g의 SFPg의 리턴 주소g의 인자 값f의 처리에 필요한 임시 정보



프로세스에서 현재 실행 중인 함수의 스택 프레임 위치를 기억하기 위해 프로세서에서 사용되는 것이 '''프레임 포인터'''이며, 구체적으로는 (현재 실행 중인 함수가 g라면) g의 SFP의 주소를 가리킨다. '''SFP'''는 함수 호출 시 호출하는 측 함수 프레임 포인터의 주소를 기억하기 위한 영역으로, f가 g를 호출했을 때, 스택 프레임 값 (=f의 SFP의 주소)을 g의 SFP에 저장한다. SFP는 "Saved Frame Pointer"의 약자로, 한국어 번역은 "'''저장된 프레임 포인터'''"이다.

한편 g의 '''리턴 주소'''는 호출된 함수 f의 '''프로그램 카운터'''의 주소를 저장한다.

예를 들어, 함수 g가 사용자로부터 (ASCII 코드) 문자열을 입력받아 입력된 문자열을 배열 char A[10]에 저장한다고 가정한다. A의 크기는 10이므로, 함수 g의 프로그래머는 사용자로부터 받은 문자열을 A에 저장하기 전에, 해당 문자열이 실제로 10자 이하인지 확인하는 메커니즘을 g에 구현해야 한다. 이러한 확인 메커니즘 구현을 잊었을 경우, 악의적인 사용자 (이하, "공격자"라고 한다)에 의해 스택 기반 버퍼 오버플로 공격을 받을 위험이 있다.

구체적으로, 공격자는 다음과 같은 문자열을 g에 입력한다:

>('''셸 코드''')…('''셸 코드의 가상 주소''')

여기서 '''셸 코드'''란, 어떤 악의적인 짧은 프로그램으로, 예를 들어 공격자를 위해 백도어를 열거나 멀웨어를 다운로드하는 것이다.

이 문자열이 배열 A의 선두부터 순서대로 기록되면, '''A[i]의 주소는 i가 클수록 커지므로''', 공격자가 입력 문자열의 길이를 적절하게 선택하면, 주소 공간에는 다음과 같이 데이터가 기록되고, '''g의 리턴 주소가 셸 코드의 가상 주소로 덮어씌워진다''':

낮은 주소높은 주소
g의 스택 프레임f의 스택 프레임
g의 처리에 필요한 임시 정보g의 지역 변수g의 SFPg의 리턴 주소g의 인자 값f의 처리에 필요한 임시 정보
(셸 코드)…(셸 코드의 가상 주소)



따라서 함수 g가 종료될 때, g의 리턴 주소 (의 위치에 덮어씌워진 셸 코드의 가상 주소)가 읽히므로, 프로그램 카운터는 셸 코드의 위치로 점프하여, 공격자의 의도대로 셸 코드가 실행된다.

아래의 예에서 "X"는 프로그램이 실행될 때 스택에 있게 되는 데이터이다. 프로그램은 그 다음 작은 양의 저장공간만 필요한 함수 "Y"를 호출한다. 그리고 "Y"는 그 다음 많은 버퍼를 필요로 하는 "Z"를 호출한다.

ZYX
      :///



만약 함수 Z가 오버플로를 발생시키면, 그것은 함수 Y나 주 프로그램에 포함된 데이터를 덮어 쓸 수도 있다:

ZYX
........//


3. 1. 1. 스택 기반 이용 예

아래의 예에서 "X"는 프로그램이 실행될 때 스택에 있게 되는 데이터이다. 프로그램은 그 다음 작은 양의 저장공간만 필요한 함수 "Y"를 호출한다. 그리고 "Y"는 그 다음 많은 버퍼를 필요로 하는 "Z"를 호출한다.

ZYX
      :///



만약 함수 Z가 오버플로를 발생시키면, 그것은 함수 Y나 주 프로그램에 포함된 데이터를 덮어 쓸 수도 있다:

ZYX
........//



이것은 대부분의 시스템에서 현재의 프로세스가 호출되기 전 실행되고 있던 프로그램 부분의 위치인 반환값을 스택이 갖고 있기 때문에 특히나 심각하다. 함수가 끝날 때 임시 저장소는 스택에서 제거되고 실행은 반환 주소로 되돌아간다. 그런데 반환 주소를 버퍼 오버플로가 덮어쓰면 어떤 다른 위치를 가리킬 것이다. 처음 예제에서와 같은 버퍼 오퍼플로가 우연히 발생하는 경우 거의 틀림없이 쓸모없는 위치일 것이다. 어떠한 프로그램 명령어 위치가 아닌 이상, 프로세스는 망가질 것이다. 그리고 악의적인 공격자가 시스템 보안과 충돌할 수 있는 임의 위치로 반환 주소를 바꿀 수 있다.

스택 버퍼 오버플로우를 악용하여 프로그램을 조작할 수 있는 몇 가지 방법이 있다.


  • 취약한 버퍼 근처에 위치한 지역 변수를 덮어써서 프로그램 동작을 변경한다.
  • 스택 프레임의 반환 주소를 덮어써서 공격자가 선택한 코드(일반적으로 쉘코드라고 함)를 가리키도록 한다. 함수가 반환되면 실행은 공격자의 쉘코드에서 재개된다.
  • 함수 포인터[1] 또는 예외 처리기를 덮어써서 쉘코드를 가리키게 한 다음 쉘코드를 실행한다.
  • 나중에 해당 프레임을 소유한 함수에서 사용될 다른 스택 프레임의 지역 변수(또는 포인터)를 덮어쓴다.[2]


공격자는 이러한 익스플로잇 중 하나를 일으키도록 데이터를 설계한 다음 취약한 코드가 사용자에게 제공하는 버퍼에 이 데이터를 배치한다. 스택 버퍼 오버플로우에 영향을 주기 위해 사용된 사용자 제공 데이터의 주소를 예측할 수 없는 경우, 원격 코드 실행을 일으키기 위해 스택 버퍼 오버플로우를 악용하는 것은 훨씬 더 어려워진다. 이러한 버퍼 오버플로우를 악용하는 데 사용할 수 있는 한 가지 기술은 "트램폴린"이라고 한다. 여기서 공격자는 취약한 스택 버퍼에 대한 포인터를 찾고 해당 포인터에 상대적인 쉘코드의 위치를 계산한다. 그런 다음 공격자는 덮어쓰기를 사용하여 메모리에 이미 있는 명령어로 점프하여 이번에는 포인터에 상대적인 두 번째 점프를 수행한다. 두 번째 점프는 쉘코드 내에서 실행 분기를 한다. 적절한 명령어는 대규모 코드에 종종 존재한다. 예를 들어 Metasploit Project는 적절한 opcode 데이터베이스를 유지 관리하지만 윈도우 운영 체제에서 발견된 opcode만 나열한다.[3]

C 언어나 C++로 작성된 프로그램의 경우, 사용자 공간을 더 세분화한다. 이들 언어로 작성된 프로그램에서는 가상 주소의 최저위부터 순서대로 프로그램의 실행 코드를 배치하는 '''코드 영역 (텍스트 영역이라고도 함)''', 초기화된 정적 변수, 전역 변수를 배치하는 '''데이터 영역''', 초기화되지 않은 정적 변수, 전역 변수를 배치하는 '''bss 영역'''을 확보한다.

한편, 사용자 공간의 가상 주소 최고위는 함수의 호출 스택을 저장하는 (가변 크기의) '''스택 영역'''으로 사용된다.

최저위최고위
코드 영역정적 영역힙 영역스택 영역
데이터 영역bss 영역



스택 영역은 프로세스 내에서 호출되는 함수의 호출 스택을 저장하는 영역으로, 호출 스택 내의 각 함수 데이터 ('''스택 프레임'''이라고 함)를 나란히 저장한다.프로세스 내에서 함수 f가 함수 g를 호출한 경우, 호출 스택은 다음과 같다:

최저 주소최고 주소
g의 스택 프레임f의 스택 프레임
g의 처리에 필요한 임시 정보g의 지역 변수g의 SFPg의 리턴 주소g의 인자 값f의 처리에 필요한 임시 정보



프로세스에서 현재 실행 중인 함수의 스택 프레임 위치를 기억하기 위해 프로세서에서 사용되는 것이 '''프레임 포인터'''이며, 구체적으로는 (현재 실행 중인 함수가 g라면) g의 SFP의 주소를 가리킨다. '''SFP'''는 함수 호출 시 호출하는 측 함수 프레임 포인터의 주소를 기억하기 위한 영역으로, f가 g를 호출했을 때, 스택 프레임 값 (=f의 SFP의 주소)을 g의 SFP에 저장한다. SFP는 "Saved Frame Pointer"의 약자로, 한국어 번역은 "'''저장된 프레임 포인터'''"이다.

한편 g의 '''리턴 주소'''는 호출된 함수 f의 '''프로그램 카운터'''의 주소를 저장한다。

예를 들어, 함수 g가 사용자로부터 (ASCII 코드) 문자열을 입력받아 입력된 문자열을 배열 char A[10]에 저장한다고 가정한다. A의 크기는 10이므로, 함수 g의 프로그래머는 사용자로부터 받은 문자열을 A에 저장하기 전에, 해당 문자열이 실제로 10자 이하인지 확인하는 메커니즘을 g에 구현해야 한다. 이러한 확인 메커니즘 구현을 잊었을 경우, 악의적인 사용자 (이하, "공격자"라고 한다)에 의해 스택 기반 버퍼 오버플로 공격을 받을 위험이 있다.

구체적으로, 공격자는 다음과 같은 문자열을 g에 입력한다:

('''셸 코드''')…('''셸 코드의 가상 주소''')
여기서 '''셸 코드'''란, 어떤 악의적인 짧은 프로그램으로, 예를 들어 공격자를 위해 백도어를 열거나 멀웨어를 다운로드하는 것이다.

이 문자열이 배열 A의 선두부터 순서대로 기록되면, '''A[i]의 주소는 i가 클수록 커지므로''', 공격자가 입력 문자열의 길이를 적절하게 선택하면, 주소 공간에는 다음과 같이 데이터가 기록되고, '''g의 리턴 주소가 셸 코드의 가상 주소로 덮어씌워진다''':

낮은 주소높은 주소
g의 스택 프레임f의 스택 프레임
g의 처리에 필요한 임시 정보g의 지역 변수g의 SFPg의 리턴 주소g의 인자 값f의 처리에 필요한 임시 정보
(셸 코드)…(셸 코드의 가상 주소)



따라서 함수 g가 종료될 때, g의 리턴 주소 (의 위치에 덮어씌워진 셸 코드의 가상 주소)가 읽히므로, 프로그램 카운터는 셸 코드의 위치로 점프하여, 공격자의 의도대로 셸 코드가 실행된다.

3. 2. 힙 기반 이용

힙 데이터 영역에서 일어나는 버퍼 오버플로를 힙 오버플로라 부르며 스택 기반 오버플로와는 다른 방법으로 이용할 수 있다.[4] 힙 상의 메모리는 동적으로 응용 프로그램에 의해 실행시간 중에 할당되고 보통 프로그램 데이터를 보관하고 있다. 이용하는 방법은 이 데이터를 특정한 방법으로 오염시켜 응용 프로그램이 연결 리스트 포인터(linked list pointer)등과 같은 내부 자료 구조를 덮어쓰게 한다.[4] 기본적인 힙 오버플로 기술은 동적 메모리 할당 연결(malloc 상위 수준 데이터)을 덮어씀으로써 프로그램 함수 포인터를 조작한다.[4] 마이크로소프트 GDI+JPEG를 처리하는 과정에서 발생하는 취약점은 힙 오버플로우가 제시할 수 있는 위험의 한 예이다.[4]

malloc 등에서 히프 영역에 동적으로 메모리를 할당하는 함수에 대한 오버플로 공격이다.[43] 기본적인 공격 수법으로는, 함수가 히프에 할당한 메모리 영역이 2개 있을 때, 그 중 한쪽 영역에 대해 할당된 메모리 크기보다 큰 데이터를 입력하여 오버플로를 일으켜, 다른 쪽 메모리 영역을 덮어쓰는 것이다.

더욱 고도화된 힙 기반 버퍼 오버플로우 공격으로, malloc이 메모리 관리에 사용하는 메타데이터를 변조하는 기법이 있다. 예를 들어, 윈도우 XP SP1 또는 그 이전 버전의 윈도우에서는, malloc으로 할당된 메모리 영역을 오버플로우시켜, 해당 메모리 영역의 (가상 주소 공간에서) 인접한 사용되지 않은 chunk의 flink 및 blink를 임의의 주소로 덮어쓰는 공격이 가능했다. flink 및 blink는 coalesce 시점에 malloc에 의해 참조되므로 공격이 성공한다.

3. 3. 이용당하는 것을 막는 장벽

버퍼가 읽히거나 실행되기 전에 버퍼를 조작하면 이용하려는 시도를 막을 수도 있다.[5] 조작에는 대문자로 또는 소문자로 변환, 제어 부호 제거, 숫자 영문자 제거 등이 포함된다.[5] 이러한 방지 수단을 회피하는 기술도 존재한다: 숫자 영문자 코드, 다형성 코드, 자체 수정 코드, libc 귀환 공격 등.[5] 같은 방법을 침입 감지 시스템에 의한 피탐지를 회피하는 데도 사용할 수 있다.[5] 어떤 경우에는, 유니코드 변환의 경우도 포함되지만, 서비스 거부만 가능한 것으로 취약점의 위협이 잘못 표현되었지만 사실은 임의 코드의 원격 실행이 가능할 수도 있다.[5]

3. 4. 이용의 실제

실제 공격에서는 주소의 무효값 바이트, 셸 코드 위치 가변성, 환경 간 상이성, 대응 수단 등 다양한 요소를 고려해야 한다.

실제 익스플로잇에서는 익스플로잇이 안정적으로 작동하기 위해 극복해야 할 다양한 과제가 있다.

함수 g에 삽입할 공격용 문자열은 "NOP 스레드 + 셸 코드 + 반환 주소 반복" 형식을 가지며, g의 반환 주소가 "반환 주소 반복" 부분에 도달하지 않으면 공격이 성공하지 않는다. 따라서 함수 g에 공격용 문자열을 삽입하는 위치와 g의 반환 주소가 (가상 주소 공간에서) 너무 가까우면 공격에 필요한 길이의 셸 코드를 삽입할 수 없는 문제가 공격자에게 발생한다.

그러나 공격자가 공격 대상 머신의 환경 변수를 설정할 수 있는 상황에서는 공격자는 이 문제를 회피한 공격이 가능하다. 대상 머신에서 프로세스가 실행될 때, 해당 프로세스의 가상 주소 공간에 환경 변수가 로드되므로, 공격자가 사전에 대상 머신의 환경 변수에 "NOP 스레드 + 셸 코드"를 써 넣으면 프로세스의 가상 주소에 "NOP 스레드 + 셸 코드"가 생성된다. 프로세스 내에서 함수 g가 실행될 때, 공격자는 공격용 문자열을 g에 입력하고 반환 주소를 해당 NOP 스레드로 덮어쓰면 공격이 성공하게 된다.

더욱 확실한 공격 방법으로, 공격 프로그램 h 내에 환경 변수를 읽는 함수(getenv() 등)를 사용하는 방법도 있다.

3. 4. 1. NOP 슬라이드 기법

NOP 슬라이드 기법은 가장 오래되고 가장 널리 알려진 스택 버퍼 오버플로 공격 기법이다. 이 기법은 NOP (No Operation) 기계어 코드를 이용하여 표적 영역의 크기를 효과적으로 늘려 공격 성공률을 높인다. 공격자는 스택의 더 큰 영역을 NOP 명령어로 채우고, 공격 데이터 끝에 셸코드가 있는 버퍼의 최상위 위치로 상대 점프하는 명령어를 둔다. 이 NOP 명령어의 모음을 "NOP 슬라이드"라고 부르는데, 덮어 쓴 반환 주소가 NOP 영역 내의 주소값을 가리키면 NOP 명령어들을 따라 "미끄러져" 최종적으로 셸 코드에 도달하기 때문이다. 이 기법을 사용하면 공격자는 상대적으로 작은 크기의 셸 코드 위치 대신 스택에서 NOP 슬라이드의 위치를 추정하면 된다.

스택에서 NOP 슬라이드 페이로드의 그림


NOP 슬라이드는 자주 사용되므로, 많은 침입 방지 시스템에서 NOP 기계어 명령어 패턴을 탐색하여 셸코드 사용을 탐지한다.[50] NOP 슬라이드는 반드시 전통적인 NOP 기계어 코드만 포함하는 것은 아니며, 셸코드 실행에 영향을 주지 않는 범위 내에서 어떤 명령어도 하드웨어 보조 NOP 자리에 사용될 수 있다. 따라서 공격자는 NOP 슬라이드를 셸코드 실행에 지장이 없는 임의로 선택한 명령어로 채우는 것이 일반적이다.

이 방법은 공격 성공 확률을 크게 높이지만, 몇 가지 문제점이 있다.

  • 이 기법을 이용한 공격은 스택 상의 거리 오프셋(offset)을 추정하여 NOP 슬라이드 영역에 들어가게 해야 하므로 어느 정도 운에 의존한다. 잘못 추정하면 대상 프로그램이 깨지고 시스템 관리자에게 공격을 알릴 수 있다.
  • 효과적인 NOP 슬라이드를 만들기 위해서는 할당받을 수 있는 메모리에 비해 너무 커야 할 수 있다. 할당된 버퍼 크기가 작고 현재 스택의 깊이가 얕을 때 문제가 될 수 있다.


이러한 문제점에도 불구하고, NOP 슬라이드는 주어진 플랫폼, 환경, 상황에 따라 유일하게 작동하는 방법일 수 있으므로 여전히 중요한 기법이다.

3. 4. 2. 레지스터에 저장된 주소로 건너뛰기

레지스터로 건너뛰기(jump to register) 기법은 NOP 슬라이드와 같은 추가 공간이나 스택 위치 오프셋을 추정하지 않아도 잘 작동한다. 이 전략은 복귀 포인터를 덮어써서 통제된 버퍼, 즉 셸코드를 담고 있는 레지스터의 위치로 프로그램이 건너뛰게 만드는 것이다. 예를 들어 레지스터 A가 어떤 버퍼의 시작점을 담고 있다면, 이 레지스터를 오퍼랜드(operand)로 삼은 어떠한 건너뛰기 또는 호출로 실행 흐름의 제어권을 얻을 수 있다.[79] 실전에서는 어떤 프로그램이 의도적으로는 어떤 특정 레지스터에 저장된 주소로 건너뛰는 명령을 포함하지 않을 수도 있다. 전통적인 해법은 어떤 의도되지 않은 적당한 작동코드 opcode 사례를 프로그램 메모리 어딘가 고정된 위치에서 찾는 것이다.

`DbgPrint()` 루틴을 호출하기 위한 ntdll.dll의 명령어는 `jmp esp`에 대한 i386 기계 연산 코드를 포함한다.


예를 들면, 뜻하지 않게 i386 JMP esp 명령이 포함된 경우가 ntdll.dll 안의 DbgPrint()함수에 있었다. 이 명령의 작동 코드는 FF E4이다. 이 연속 두 바이트 패턴이 call DbgPrint 명령에서 1Byte 떨어진 곳 주소 0x7C941EED에 나타났다. 공격자가 프로그램의 귀환 주소를 이 주소로 덮어 쓰면, 프로그램이 처음에는 0x7C941EED로 건너뛰어 실행 코드 FF E4를 JMP esp 명령으로 인식하여 스택의 정상 위치로 jump, 공격자의 코드를 실행할 것이다.

이 기법이 가능하다면 취약성의 심각성이 상당히 증가한다. 이는 왜냐하면 이용이 충분히 신뢰성 있게 작동하여 공격을 자동화, 실행하면 가상적으로 성공을 보증할 수 있게 되기 때문이다. 이러한 이유로, 이 기법이 인터넷 웜에서 가장 흔히 버퍼 오버플로 취약점을 이용하기 위해 사용된다.

이 방법은 또한 셸코드의 위치를 윈도 플랫폼 상에서 덮어씌어진 귀환 주소 뒤에 위치하더라고 가능하다. 실행 코드는 주소 0x00400000에 기반하고, x86은 리틀 엔디안 구조이므로 귀환 주소의 최후 바이트는 반드시 무효값이 되고, 이것이 버퍼 복사를 마감하며 그 이후에는 아무것도 씌지 않는다. 이로 하여 셸코드의 크기는 버퍼의 크기로 제한되며, 지나친 규제가 될 수 있다. DLL은 고위 메모리 (0x01000000 이상)에 저장되며 따라서 무효 바이트가 없는 주소를 가지므로, 이 방법은 무효 바이트 (또는 다른 허용 안되는 문자)를 덮어씌어진 복귀 주소에서 제거할 수 있다. 이런 식으로 사용되면, 이 방법은 때때로 DLL 프램폴라이닝이라고 불린다.

공격자가 입력한 데이터(익스플로잇 등)의 주소를 정확히는 알수 없으나 해당 주소가 레지스터에 저장되어 있다는 것을 알고 있는 경우에는 ''트램폴리닝''(trampolining)이라고 불리는 기법이 사용된다. 이 기법에서는 공격자가 입력한 데이터로 점프하는 연산 코드의 주소를 반환 주소에 덮어쓴다. 예를 들어 주소가 레지스터 R에 저장되어 있는 경우, jump R(혹은 call R 등)이라는 연산 코드가 저장된 주소로 점프하게 하여 사용자가 입력한 데이터를 실행시킨다. 이 기법에서 사용하는 연산 코드는 DLL나 실행 파일 내의 것을 이용한다. 단, 일반적으로 연산 코드의 주소에 널 문자가 포함되어 있어서는 안 되며, 처리에 사용하는 연산 코드의 주소는 애플리케이션이나 운영 체제의 버전에 따라 다르다. 메타스플로잇 프로젝트는 이러한 목적에 적합한 연산 코드 데이터베이스 중 하나이며, Windows에서 사용할 수 있는 연산 코드가 기재되어 있다[51]

Return-to-Register 공격은 "ret 명령 실행 후 레지스터가 가리키는 주소에 부정한 명령 코드를 삽입하고, 그 위에 '해당 레지스터 값으로 실행을 옮기는 명령군이 저장된 주소'로 리턴 주소를 덮어쓰는 공격"을 말한다.

ret2esp (Return to esp) 공격은 2017년 현재 ASLR(주소 공간 배치 난수화) 구현의 기본 설정에서는 코드 영역(.text 섹션)을 난수화하지 않는 점을 이용하여 ASLR을 우회하는 공격 기법이다.[80] 여기서 esp는 x86에서 스택 포인터이다. 이 공격은 .text 섹션 내에 "jmp esp"와 같은 명령어가 있는 경우 성립한다.[80] 공격자는 버퍼 오버플로우를 이용하여 esp의 (가상 주소 공간상의) 아래에 셸 코드를 배치한 후, 반환 주소를 "jmp esp" 위치로 변경한다. 그러면 먼저 반환 주소의 변경에 의해 "jmp esp" 위치로 점프하고, 다음에 "jmp esp"가 실행되어 esp 위치로 점프하므로, 그 아래에 배치된 셸 코드가 실행된다.[80]

4. 보호 대응 수단

C/C++는 메모리 접근에 대한 내장 보호 기능이 없어 버퍼 오버플로에 취약하다.[6] C와 C++는 메모리상 어떤 부분에서도 데이터 접근과 덮어쓰기에 대한 내장 보호 기능이 없다.[6] 더 구체적으로, 데이터가 어떤 배열(버퍼의 구현)에 씌어지는 데이터가 그 배열의 범위 안에 씌어지는지 검사하지 않는다. 그러나 표준 C++ 라이브러리는 데이터를 안전히 저장하는 많은 방법을 제공하고, C언어에서 버퍼 오버플로를 회피하기 위한 기술도 존재한다.[6] 예를 들어, C++의 표준 템플릿 라이브러리(STL)는 프로그래머가 데이터 접근 시 명시적으로 경계 검사를 호출하는 경우 선택적으로 경계 검사를 수행할 수 있는 컨테이너를 제공한다. `vector`의 멤버 함수 `at()`는 경계 검사를 수행하고 경계 검사에 실패하면 `out_of_range` 예외를 발생시킨다.[7]

어셈블리, C, C++는 메모리에 직접 접근을 허용하고 강력한 형식 지정을 지원하지 않기 때문에 버퍼 오버플로에 취약한 인기 프로그래밍 언어이다.[6]

반면, COBOL, Java, Eiffel, Python 등과 같이 강력한 형식 지정을 지원하고 메모리에 직접 접근을 허용하지 않는 언어는 대부분의 경우 버퍼 오버플로를 방지한다.[6] C 또는 C++ 이외의 많은 프로그래밍 언어는 런타임 검사를 제공하며, 경우에 따라 경고를 보내거나 예외를 발생시킬 수 있는 컴파일 시간 검사도 제공한다. 이러한 언어의 예로는 에이다, Eiffel, 리스프, 모듈라-2, 스몰토크, OCaml 및 사이클론, Rust, D와 같은 C 파생 언어가 있다.[6] 자바 및 .NET Framework 바이트코드 환경 또한 모든 배열에 대한 경계 검사를 요구한다. 거의 모든 인터프리터 언어는 버퍼 오버플로를 방지하여 잘 정의된 오류 조건을 신호로 보낸다.

소프트웨어 엔지니어는 어떤 언어와 컴파일러 설정을 사용할지 결정할 때 안전성과 성능 비용 간의 트레이드 오프를 신중하게 고려해야 한다.

버퍼 오버플로는 C와 C++ 언어에서 흔히 발생하는데, 이는 이러한 언어가 데이터 형을 보관하는 버퍼의 저수준 표현 상세를 노출시키기 때문이다. 따라서 버퍼 오버플로를 방지하기 위해서는 버퍼 관리 코드 내에서 높은 수준의 정확성을 유지해야 한다. 오랫동안 `gets`, `scanf`, `strcpy` 등 경계 검사를 하지 않는 표준 라이브러리 함수 사용을 피하는 것이 권장되어 왔다.[8] 모리스 웜은 fingerd 내에서 호출되는 `gets` 함수를 이용했다.[8]

경계 검사를 포함한 버퍼 관리를 중앙 집중화하고 자동적으로 실시하는 잘 작성되고 검사된 추상 데이터형 라이브러리는 버퍼 오버플로 발생과 충격을 줄일 수 있다. 버퍼 오버플로가 자주 발생하는 언어의 두 가지 주요한 기본 데이터 형은 문자열과 배열이다. 따라서 이러한 데이터 형에서 버퍼 오버플로를 방지하는 라이브러리는 필요한 영역의 대부분을 담당해 줄 수 있다. 그러나 이러한 안전한 라이브러리를 사용하지 못하면 버퍼 오버플로 및 다른 취약점을 낳을 수 있으며, 라이브러리 자체의 버그 역시 잠재적인 취약점이 된다. "안전한" 라이브러리 구현에는 "The Better String Library",[9] Vstr,[10] Erwin[11] 등이 있다. OpenBSD 운영 체제의 C 라이브러리는 strlcpy와 strlcat 함수를 제공하지만, 이는 완전한 안전 라이브러리 구현보다는 제한적이다.

2006년 9월 C 언어 표준 위원회 기술 보고서 24731이 공개되었는데,[12] 이는 표준 C 라이브러리의 문자열과 입출력 함수에 기반하여 버퍼 크기 매개 변수를 추가로 가진 함수들을 명세하였다. 그러나 이러한 함수들의 버퍼 오버플로 방지 효력에 대해서는 논란의 여지가 있다. 프로그래머가 함수 호출마다 개입해야 하는데, 이는 더 오래된 표준 라이브러리 함수의 버퍼 오버플로를 방지하게 만드는 것과 같은 수준의 일이다.[13]

더 안전한 기능을 제공하는 함수를 사용하는 것도 한 방법이다. 예를 들어, 문자열 길이를 구하는 C 함수인 `strlen`, `wcslen` 함수는 NULL 문자를 만날 때까지 다음 메모리를 참조하는 방식으로 작동하기 때문에, 원래 문자열 끝에 NULL이 없으면 메모리 영역 끝을 넘어설 수 있다. 이때 개선된 함수인 `strlen s`나 `wcslen s`를 사용하면 참조할 최대 메모리를 제한할 수 있다. 이 외에도 기존 문자열 함수 뒤에 _s가 붙은 이름의 함수를 사용하면 버퍼 오버플로 문제를 개선할 수 있다.[87]

표준 C 라이브러리를 사용하는 대신, 버퍼 오버플로를 사전에 방지하거나 오류로 감지하는 안전한 라이브러리를 사용하는 것도 중요하다. 이러한 라이브러리로는 Managed String(리눅스 환경), ISO/IEC 9899:2011 Annex K(윈도우 환경), SafeStr(리눅스 환경, 윈도우 환경), "The Better String Library", Vstr, Erwin 등이 있다. 또한 BSD libc 등, C 라이브러리의 구현에 따라 strlcpy나 strlcat와 같은, 더 안전에 배려한 문자열용 함수가 준비되어 있다.

버퍼 오버플로 보호는 가장 일반적인 버퍼 오버플로를 검출하기 위해 사용되며 함수가 귀환할 때 스택이 변경되었는지 검사한다. 변경되었다면 프로그램이 세그멘테이션 오류를 발생 시키며 중단된다.[14] 그러한 시스템의 세가지 예가 gcc 패치인 립세이프(LibSafe),[15] 스택 가드,[16] 프로폴리스이다. 마이크로소프트의 데이터 실행 방지 모드는 명백히 SEH 예외 처리기를 가리키는 포인터를 덮어쓰기로부터 보호한다.[17] 더 강력한 스택 보호는 스택을 데이터용과 함수 귀환용으로 나누는 것이다. 이러한 구분은 포스 프로그래밍 언어에 채택되어 있지만, 이는 보안을 위한 것은 아니었다. 어쨌든, 이는 귀환 주소가 아닌 민감한 데이터는 여전히 덮어씌어질 가능성이 있어 완벽한 해결책은 아니다.

이러한 유형의 보호는 모든 공격을 감지하지 못하므로 완전히 정확하지 않다. StackGuard와 같은 시스템은 공격의 동작에 더 중점을 두고 있어 범위 검사 시스템에 비해 효율적이고 빠르다.[18]

컴파일 시 '''카나리아'''(canary)[56][57] 또는 '''쿠키'''라고 불리는 영역을 삽입하여, 프로그램 실행 중 카나리아를 계속 감시하여 버퍼 오버플로 발생시 프로그램을 중단 하는 기법이 있다.



버퍼 오버플로는 저장된 주소를 포함한 포인터를 조작하여 작동한다. PointGuard는 공격자가 포인터와 주소를 신뢰성 있게 조작하는 것을 방지하기 위해 컴파일러 확장 기능으로 제안되었다.[19] 이 접근 방식은 컴파일러가 포인터를 사용 전후에 자동으로 XOR 인코딩하는 코드를 추가하여 작동한다. 이론적으로 공격자는 포인터를 인코딩하고 디코딩하는 데 어떤 값이 사용될지 알 수 없으므로, 새로운 값으로 덮어쓸 경우 포인터가 무엇을 가리킬지 예측할 수 없다. PointGuard는 출시되지 않았지만, 윈도우 XP SP2 및 윈도우 서버 2003 SP1부터 마이크로소프트는 이와 유사한 접근 방식을 구현했다.[20] 마이크로소프트는 포인터 보호 기능을 자동 기능으로 구현하는 대신 호출할 수 있는 API 루틴을 추가했다.

XOR은 선형이기 때문에 공격자는 주소의 하위 바이트만 덮어써서 인코딩된 포인터를 조작할 수 있다. 공격자가 익스플로잇을 여러 번 시도하거나 여러 위치 중 하나(예: NOP 슬레드 내의 모든 위치)를 가리키도록 포인터를 만들어 공격을 완료할 수 있는 경우 이러한 조작이 가능하다.[21] 마이크로소프트는 부분적인 덮어쓰기에 대한 이 취약점을 해결하기 위해 인코딩 방식에 임의의 회전을 추가했다.[22]

실행 공간 보호는 버퍼 오버플로를 방지하기 위한 방법으로, 스택이나 상의 코드가 실행되는 것을 막는다. 공격자는 버퍼 오버플로를 이용하여 임의의 코드를 프로그램의 메모리에 삽입할 수 있지만, 실행 영역 보호가 있다면, 그 코드를 실행하고자 하는 어떠한 시도도 예외를 발생시킨다.[23][24][25][26][27][28]

어떤 CPU는 NX (No eXecute) 또는 XD (eXecute Disabled) 비트라는 기능을 지원하는데, 소프트웨어와 연계하여 데이터의 페이지 (즉 스택이나 힙을 담고 있는)를 읽고 쓰기는 가능하나 실행 불가로 표시할 수 있다. 오픈 BSD, OS X 같은 유닉스 운영 체제는 실행 가능한 공간 보호와 함께 출시되었다.[23][24][25] (예: W^X)

새로운 마이크로소프트 윈도우 변종도 실행 공간 보호를 지원하며 데이터 실행 방지라고 부른다.[26]

실행 공간 보호로 libc로 귀환 공격 또는 공격자 코드 실행에 의지하지 않는 공격을 방지한다고 보증할 수는 없다. 하지만, 64비트 시스템에서 주소 공간 배치 난수화(ASLR)를 사용하는 경우, 실행 공간 보호는 이러한 공격을 실행하기 더욱 어렵게 만든다.

데이터를 저장해야 할 영역에서는 실행을 불가능하게 하는 '''W^X''' ('''W\boldsymbol{\oplus}X''' 또는 '''W^X'''로 약칭)라는 대책 기법이 알려져 있다. W\oplusX의 Windows에서의 구현은 '''DEP''' (data execution prevention영어, 데이터 실행 방지)라고 불린다. 또한 DEP에서는 SEH 예외 핸들러에 대한 포인터가 덮어 쓰이지 않도록 명시적으로 보호를 수행한다.

2018년 현재 널리 사용되는 x64 아키텍처의 프로세서에서는 W\oplusX를 실현하기 위해 데이터 영역임을 식별하는 '''NX 비트'''라는 메커니즘이 하드웨어 수준에서 지원되고 있다. (인텔에서는 NX 비트를 XD 비트라고 부르지만 동일한 것이다.)

주소 공간 배치 난수화(Address space layout randomization, ASLR)는 컴퓨터 보안 기능으로 중요 데이터 영역, 예를 들어 실행 코드의 기반 주소, 라이브러리, 힙, 스택 주소 등을 임의로 프로세서의 주소 공간에 배치하는 것이다.[29] 함수와 변수를 찾을 수 있는 가상 메모리 주소의 난수화로 버퍼 오버플로 이용이 더 어려워지지만 불가능한 것은 아니다.[29] 공격자가 이용 시도를 각각의 시스템에 따라 다르게 하도록 강요하므로 인터넷 웜 방어에 더 유용하다.[29]

ASLR은 버퍼 오버플로 공격의 발전 형태인 Return-to-libc 공격을 완화할 수 있지만, Return-oriented programming에는 대응할 수 없다. 커널 공간의 ASLR을 '''KASLR''' (kernel address space layout randomization)이라고 하며, 리눅스 커널(버전 3.14 이후)[69], iOS(버전 6 이후)[70] 등에서 구현되어 있다.

gcc와 g++로 컴파일할 때 스택 영역과 힙 영역에 대해서는 ASLR을 사용하지만, 코드 영역에 ASLR을 사용하려면 옵션 "-pie"를 사용해야 한다.[71] 또한 공유 라이브러리에 ASLR을 사용하려면 옵션 "-fPIC"를 지정한다.[71]

심층 패킷 조사(DPI)는 네트워크 경계에서 공격 고유 신호와 휴리스틱을 사용하여 버퍼 오버플로를 악용하려는 매우 기본적인 원격 시도를 탐지한다. 이 기술은 알려진 공격의 시그니처를 가진 패킷을 차단할 수 있다. 이전에는 긴 일련의 No-Operation 명령(NOP-sled)이 감지되면 패킷을 막을 수 있었으며, 페이로드 위치가 약간 가변적인 상황에서 사용되었다.

패킷 스캔은 알려진 공격만 방지할 수 있고 NOP 슬라이드는 다양한 방법으로 암호화 할 수 있기 때문에 효과적이지 못한 방법이다. 공격자들은 경험론적 패킷 스캐너와 침입 감지 시스템의 탐지를 회피하기 위하여 영숫자 코드, 변형, 또는 자기 수정 방식의 셸코드를 사용하기 시작하였다.

4. 1. 프로그래밍 언어 선택

C/C++는 메모리 접근에 대한 내장 보호 기능이 없어 버퍼 오버플로에 취약하다.[6] C와 C++는 메모리상 어떤 부분에서도 데이터 접근과 덮어쓰기에 대한 내장 보호 기능이 없다.[6] 더 구체적으로, 데이터가 어떤 배열(버퍼의 구현)에 씌어지는 데이터가 그 배열의 범위 안에 씌어지는지 검사하지 않는다. 그러나 표준 C++ 라이브러리는 데이터를 안전히 저장하는 많은 방법을 제공하고, C언어에서 버퍼 오버플로를 회피하기 위한 기술도 존재한다.[6] 예를 들어, C++의 표준 템플릿 라이브러리(STL)는 프로그래머가 데이터 접근 시 명시적으로 경계 검사를 호출하는 경우 선택적으로 경계 검사를 수행할 수 있는 컨테이너를 제공한다. `vector`의 멤버 함수 `at()`는 경계 검사를 수행하고 경계 검사에 실패하면 `out_of_range` 예외를 발생시킨다.[7]

어셈블리, C, C++는 메모리에 직접 접근을 허용하고 강력한 형식 지정을 지원하지 않기 때문에 버퍼 오버플로에 취약한 인기 프로그래밍 언어이다.[6]

반면, COBOL, Java, Eiffel, Python 등과 같이 강력한 형식 지정을 지원하고 메모리에 직접 접근을 허용하지 않는 언어는 대부분의 경우 버퍼 오버플로를 방지한다.[6] C 또는 C++ 이외의 많은 프로그래밍 언어는 런타임 검사를 제공하며, 경우에 따라 경고를 보내거나 예외를 발생시킬 수 있는 컴파일 시간 검사도 제공한다. 이러한 언어의 예로는 에이다, Eiffel, 리스프, 모듈라-2, 스몰토크, OCaml 및 사이클론, Rust, D와 같은 C 파생 언어가 있다.[6] 자바 및 .NET Framework 바이트코드 환경 또한 모든 배열에 대한 경계 검사를 요구한다. 거의 모든 인터프리터 언어는 버퍼 오버플로를 방지하여 잘 정의된 오류 조건을 신호로 보낸다.

소프트웨어 엔지니어는 어떤 언어와 컴파일러 설정을 사용할지 결정할 때 안전성과 성능 비용 간의 트레이드 오프를 신중하게 고려해야 한다.

4. 2. 안전한 라이브러리 사용

버퍼 오버플로는 C와 C++ 언어에서 흔히 발생하는데, 이는 이러한 언어가 데이터 형을 보관하는 버퍼의 저수준 표현 상세를 노출시키기 때문이다. 따라서 버퍼 오버플로를 방지하기 위해서는 버퍼 관리 코드 내에서 높은 수준의 정확성을 유지해야 한다. 오랫동안 gets, scanf, strcpy 등 경계 검사를 하지 않는 표준 라이브러리 함수 사용을 피하는 것이 권장되어 왔다.[8] 모리스 웜은 fingerd 내에서 호출되는 gets 함수를 이용했다.[8]

경계 검사를 포함한 버퍼 관리를 중앙 집중화하고 자동적으로 실시하는 잘 작성되고 검사된 추상 데이터형 라이브러리는 버퍼 오버플로 발생과 충격을 줄일 수 있다. 버퍼 오버플로가 자주 발생하는 언어의 두 가지 주요한 기본 데이터 형은 문자열과 배열이다. 따라서 이러한 데이터 형에서 버퍼 오버플로를 방지하는 라이브러리는 필요한 영역의 대부분을 담당해 줄 수 있다. 그러나 이러한 안전한 라이브러리를 사용하지 못하면 버퍼 오버플로 및 다른 취약점을 낳을 수 있으며, 라이브러리 자체의 버그 역시 잠재적인 취약점이 된다. "안전한" 라이브러리 구현에는 "The Better String Library",[9] Vstr,[10] Erwin[11] 등이 있다. OpenBSD 운영 체제의 C 라이브러리는 strlcpy와 strlcat 함수를 제공하지만, 이는 완전한 안전 라이브러리 구현보다는 제한적이다.

2006년 9월 C 언어 표준 위원회 기술 보고서 24731이 공개되었는데,[12] 이는 표준 C 라이브러리의 문자열과 입출력 함수에 기반하여 버퍼 크기 매개 변수를 추가로 가진 함수들을 명세하였다. 그러나 이러한 함수들의 버퍼 오버플로 방지 효력에 대해서는 논란의 여지가 있다. 프로그래머가 함수 호출마다 개입해야 하는데, 이는 더 오래된 표준 라이브러리 함수의 버퍼 오버플로를 방지하게 만드는 것과 같은 수준의 일이다.[13]

더 안전한 기능을 제공하는 함수를 사용하는 것도 한 방법이다. 예를 들어, 문자열 길이를 구하는 C 함수인 strlen, wcslen 함수는 NULL 문자를 만날 때까지 다음 메모리를 참조하는 방식으로 작동하기 때문에, 원래 문자열 끝에 NULL이 없으면 메모리 영역 끝을 넘어설 수 있다. 이때 개선된 함수인 strlen swcslen s를 사용하면 참조할 최대 메모리를 제한할 수 있다. 이 외에도 기존 문자열 함수 뒤에 _s가 붙은 이름의 함수를 사용하면 버퍼 오버플로 문제를 개선할 수 있다.[87]

표준 C 라이브러리를 사용하는 대신, 버퍼 오버플로를 사전에 방지하거나 오류로 감지하는 안전한 라이브러리를 사용하는 것도 중요하다. 이러한 라이브러리로는 Managed String(리눅스 환경), ISO/IEC 9899:2011 Annex K(윈도우 환경), SafeStr(리눅스 환경, 윈도우 환경), "The Better String Library", Vstr, Erwin 등이 있다. 또한 BSD libc 등, C 라이브러리의 구현에 따라 strlcpy나 strlcat와 같은, 더 안전에 배려한 문자열용 함수가 준비되어 있다.

4. 3. 버퍼 오버플로 보호

버퍼 오버플로 보호는 가장 일반적인 버퍼 오버플로를 검출하기 위해 사용되며 함수가 귀환할 때 스택이 변경되었는지 검사한다. 변경되었다면 프로그램이 세그멘테이션 오류를 발생 시키며 중단된다.[14] 그러한 시스템의 세가지 예가 gcc 패치인 립세이프(LibSafe),[15] 스택 가드,[16] 프로폴리스이다. 마이크로소프트의 데이터 실행 방지 모드는 명백히 SEH 예외 처리기를 가리키는 포인터를 덮어쓰기로부터 보호한다.[17] 더 강력한 스택 보호는 스택을 데이터용과 함수 귀환용으로 나누는 것이다. 이러한 구분은 포스 프로그래밍 언어에 채택되어 있지만, 이는 보안을 위한 것은 아니었다. 어쨌든, 이는 귀환 주소가 아닌 민감한 데이터는 여전히 덮어씌어질 가능성이 있어 완벽한 해결책은 아니다.

이러한 유형의 보호는 모든 공격을 감지하지 못하므로 완전히 정확하지 않다. StackGuard와 같은 시스템은 공격의 동작에 더 중점을 두고 있어 범위 검사 시스템에 비해 효율적이고 빠르다.[18]

컴파일 시 '''카나리아'''(canary)[56][57] 또는 '''쿠키'''라고 불리는 영역을 삽입하여, 프로그램 실행 중 카나리아를 계속 감시하여 버퍼 오버플로 발생시 프로그램을 중단 하는 기법이 있다.

4. 4. 포인터 보호

버퍼 오버플로는 저장된 주소를 포함한 포인터를 조작하여 작동한다. PointGuard는 공격자가 포인터와 주소를 신뢰성 있게 조작하는 것을 방지하기 위해 컴파일러 확장 기능으로 제안되었다.[19] 이 접근 방식은 컴파일러가 포인터를 사용 전후에 자동으로 XOR 인코딩하는 코드를 추가하여 작동한다. 이론적으로 공격자는 포인터를 인코딩하고 디코딩하는 데 어떤 값이 사용될지 알 수 없으므로, 새로운 값으로 덮어쓸 경우 포인터가 무엇을 가리킬지 예측할 수 없다. PointGuard는 출시되지 않았지만, 윈도우 XP SP2 및 윈도우 서버 2003 SP1부터 마이크로소프트는 이와 유사한 접근 방식을 구현했다.[20] 마이크로소프트는 포인터 보호 기능을 자동 기능으로 구현하는 대신 호출할 수 있는 API 루틴을 추가했다.

XOR은 선형이기 때문에 공격자는 주소의 하위 바이트만 덮어써서 인코딩된 포인터를 조작할 수 있다. 공격자가 익스플로잇을 여러 번 시도하거나 여러 위치 중 하나(예: NOP 슬레드 내의 모든 위치)를 가리키도록 포인터를 만들어 공격을 완료할 수 있는 경우 이러한 조작이 가능하다.[21] 마이크로소프트는 부분적인 덮어쓰기에 대한 이 취약점을 해결하기 위해 인코딩 방식에 임의의 회전을 추가했다.[22]

4. 5. 실행 공간 보호

실행 공간 보호는 버퍼 오버플로를 방지하기 위한 방법으로, 스택이나 상의 코드가 실행되는 것을 막는다. 공격자는 버퍼 오버플로를 이용하여 임의의 코드를 프로그램의 메모리에 삽입할 수 있지만, 실행 영역 보호가 있다면, 그 코드를 실행하고자 하는 어떠한 시도도 예외를 발생시킨다.[23][24][25][26][27][28]

어떤 CPU는 NX (No eXecute) 또는 XD (eXecute Disabled) 비트라는 기능을 지원하는데, 소프트웨어와 연계하여 데이터의 페이지 (즉 스택이나 힙을 담고 있는)를 읽고 쓰기는 가능하나 실행 불가로 표시할 수 있다. 오픈 BSD, OS X 같은 유닉스 운영 체제는 실행 가능한 공간 보호와 함께 출시되었다.[23][24][25] (예: W^X)

새로운 마이크로소프트 윈도우 변종도 실행 공간 보호를 지원하며 데이터 실행 방지라고 부른다.[26]

실행 공간 보호로 libc로 귀환 공격 또는 공격자 코드 실행에 의지하지 않는 공격을 방지한다고 보증할 수는 없다. 하지만, 64비트 시스템에서 주소 공간 배치 난수화(ASLR)를 사용하는 경우, 실행 공간 보호는 이러한 공격을 실행하기 더욱 어렵게 만든다.

데이터를 저장해야 할 영역에서는 실행을 불가능하게 하는 '''W^X''' ('''W\boldsymbol{\oplus}X''' 또는 '''W^X'''로 약칭)라는 대책 기법이 알려져 있다. W\oplusX의 Windows에서의 구현은 '''DEP''' (data execution prevention영어, 데이터 실행 방지)라고 불린다. 또한 DEP에서는 SEH 예외 핸들러에 대한 포인터가 덮어 쓰이지 않도록 명시적으로 보호를 수행한다.

2018년 현재 널리 사용되는 x64 아키텍처의 프로세서에서는 W\oplusX를 실현하기 위해 데이터 영역임을 식별하는 '''NX 비트'''라는 메커니즘이 하드웨어 수준에서 지원되고 있다. (인텔에서는 NX 비트를 XD 비트라고 부르지만 동일한 것이다.)

4. 6. 주소 공간 배치 난수화

주소 공간 배치 난수화(Address space layout randomization, ASLR)는 컴퓨터 보안 기능으로 중요 데이터 영역, 예를 들어 실행 코드의 기반 주소, 라이브러리, 힙, 스택 주소 등을 임의로 프로세서의 주소 공간에 배치하는 것이다.[29] 함수와 변수를 찾을 수 있는 가상 메모리 주소의 난수화로 버퍼 오버플로 이용이 더 어려워지지만 불가능한 것은 아니다.[29] 공격자가 이용 시도를 각각의 시스템에 따라 다르게 하도록 강요하므로 인터넷 웜 방어에 더 유용하다.[29]

ASLR은 버퍼 오버플로 공격의 발전 형태인 Return-to-libc 공격을 완화할 수 있지만, Return-oriented programming에는 대응할 수 없다. 커널 공간의 ASLR을 '''KASLR''' (kernel address space layout randomization)이라고 하며, 리눅스 커널(버전 3.14 이후)[69], iOS(버전 6 이후)[70] 등에서 구현되어 있다.

gcc와 g++로 컴파일할 때 스택 영역과 힙 영역에 대해서는 ASLR을 사용하지만, 코드 영역에 ASLR을 사용하려면 옵션 "-pie"를 사용해야 한다.[71] 또한 공유 라이브러리에 ASLR을 사용하려면 옵션 "-fPIC"를 지정한다.[71]

4. 7. 심층 패킷 조사

심층 패킷 조사(DPI)는 네트워크 경계에서 공격 고유 신호와 휴리스틱을 사용하여 버퍼 오버플로를 악용하려는 매우 기본적인 원격 시도를 탐지한다. 이 기술은 알려진 공격의 시그니처를 가진 패킷을 차단할 수 있다. 이전에는 긴 일련의 No-Operation 명령(NOP-sled)이 감지되면 패킷을 막을 수 있었으며, 페이로드 위치가 약간 가변적인 상황에서 사용되었다.

패킷 스캔은 알려진 공격만 방지할 수 있고 NOP 슬라이드는 다양한 방법으로 암호화 할 수 있기 때문에 효과적이지 못한 방법이다. 공격자들은 경험론적 패킷 스캐너와 침입 감지 시스템의 탐지를 회피하기 위하여 영숫자 코드, 변형, 또는 자기 수정 방식의 셸코드를 사용하기 시작하였다.

5. 이용의 역사

버퍼 오버플로는 1972년 컴퓨터 보안 기술 기획 연구에서 처음 소개된 이후로 알려진 개념이다.[32] 당시 연구에서는 "이 기능을 수행하는 코드는 출처와 목적지 주소를 적절히 검사하지 않으므로, 사용자가 모니터(오늘날의 커널)의 일부를 덮어쓰도록 허용한다. 이는 코드를 모니터에 삽입하여 제어권을 획득하는 데 사용될 수 있다."라고 설명했다.[81]

1980년대 개인용 컴퓨터가 확산되면서 버퍼 오버플로에 대한 인식이 높아졌다. 초기 마이크로소프트 윈도우 운영 체제는 버퍼 보호에 취약하여 많은 프로그래머들이 버퍼 오버플로를 인지하게 되었다.

최초로 문서화된 악의적인 버퍼 오버플로 이용은 1988년 모리스 웜이다.[33] 모리스 웜은 finger 서비스의 취약점을 이용했다.[81] 1995년 토마스 로파틱은 버퍼 오버플로를 독립적으로 재발견하고 버그트랙에 발표했다.[34][82] 1996년 엘리아스 레비는 프랙에 스택 기반 버퍼 오버플로 공격 방법을 단계별로 소개했다.[35][83]

이후, 2001년 코드 레드 웜은 인터넷 정보 서비스 5.0의 버퍼 오버플로를,[36][84] 2003년 SQL 슬래머 웜은 마이크로소프트 SQL 서버 2000의 버퍼 오버플로를 이용했다.[37][85]

2003년에는 엑스박스 게임, 플레이스테이션 2, 닌텐도 위 등 게임 콘솔에서도 버퍼 오버플로 취약점을 이용한 공격이 발생했다.[38][86]

6. 한국의 정보 보안 환경

참조

[1] 웹사이트 CORE-2007-0219: OpenBSD's IPv6 mbufs remote kernel buffer overflow http://www.securityf[...] 2007-05-15
[2] 웹사이트 Modern Overflow Targets http://packetstormse[...] 2013-07-05
[3] 웹사이트 The Metasploit Opcode Database https://web.archive.[...] 2007-05-15
[4] 웹사이트 Microsoft Technet Security Bulletin MS04-028 https://web.archive.[...] 2007-05-15
[5] 웹사이트 Creating Arbitrary Shellcode In Unicode Expanded Strings https://web.archive.[...] 2007-05-15
[6] Webarchive Buffer Overflows article on OWASP https://www.owasp.or[...] 2016-08-29
[7] 웹사이트 vector::at - C++ Reference http://www.cplusplus[...] Cplusplus.com 2014-03-27
[8] 웹사이트 Archived copy https://web.archive.[...] 2022-06-06
[9] 웹사이트 The Better String Library http://bstring.sf.ne[...]
[10] 웹사이트 The Vstr Homepage https://web.archive.[...] 2007-05-15
[11] 웹사이트 The Erwin Homepage http://www.theiling.[...] 2007-05-15
[12] 웹사이트 Information technology – Programming languages, their environments and system software interfaces – Extensions to the C library – Part 1: Bounds-checking interfaces https://www.iso.org/[...] 2007
[13] 웹사이트 CERT Secure Coding Initiative https://archive.toda[...] 2007-07-30
[14] 웹사이트 Libsafe at FSF.org http://directory.fsf[...] 2007-05-20
[15] 웹사이트 StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al. https://www.usenix.o[...] 2007-05-20
[16] 웹사이트 ProPolice at X.ORG https://web.archive.[...] 2007-05-20
[17] 웹사이트 Bypassing Windows Hardware-enforced Data Execution Prevention https://web.archive.[...] 2007-05-20
[18] 간행물 Buffer overflow and format string overflow vulnerabilities https://onlinelibrar[...] 2003-04-25
[19] 웹사이트 12th USENIX Security Symposium – Technical Paper http://www.usenix.or[...] 2018-04-03
[20] 웹사이트 Protecting against Pointer Subterfuge (Kinda!) https://web.archive.[...] 2018-04-03
[21] 웹사이트 USENIX - The Advanced Computing Systems Association http://www.usenix.or[...] 2018-04-03
[22] 웹사이트 Protecting against Pointer Subterfuge (Redux) https://web.archive.[...] 2018-04-03
[23] 웹사이트 PaX: Homepage of the PaX team http://pax.grsecurit[...] 2007-06-03
[24] 웹사이트 KernelTrap.Org https://archive.toda[...] 2007-06-03
[25] 웹사이트 Openwall Linux kernel patch 2.4.34-ow1 https://web.archive.[...] 2007-06-03
[26] 웹사이트 Microsoft Technet: Data Execution Prevention https://web.archive.[...] 2006-06-30
[27] 웹사이트 BufferShield: Prevention of Buffer Overflow Exploitation for Windows http://www.sys-manag[...] 2007-06-03
[28] 웹사이트 NGSec Stack Defender http://www.ngsec.com[...] 2007-06-03
[29] 웹사이트 PaX at GRSecurity.net http://pax.grsecurit[...] 2007-06-03
[30] 웹사이트 The Exploitant - Security info and tutorials http://raykoid666.wo[...] 2009-11-29
[31] 간행물 Statically Detecting Likely Buffer Overflow Vulnerabilities https://www.usenix.o[...] 2001-08-13
[32] 웹사이트 Computer Security Technology Planning Study https://web.archive.[...] 2007-11-02
[33] 웹사이트 "A Tour of The Worm" by Donn Seeley, University of Utah http://world.std.com[...] 2007-06-03
[34] 웹사이트 Bugtraq security mailing list archive http://www.security-[...] 2007-06-03
[35] 웹사이트 "Smashing the Stack for Fun and Profit" by Aleph One http://www.phrack.co[...] 2012-09-05
[36] 웹사이트 eEye Digital Security https://web.archive.[...] 2007-06-03
[37] 웹사이트 Microsoft Technet Security Bulletin MS02-039 https://web.archive.[...] 2007-06-03
[38] 웹사이트 Hacker breaks Xbox protection without mod-chip http://www.gamesindu[...] 2007-06-03
[39] 웹사이트 第10章 著名な脆弱性対策バッファオーバーフロー: #1 概要 https://www.ipa.go.j[...] 정보처리추진기구 2018-12-14
[40] 웹사이트 '[迷信] scanf ではバッファオーバーランを防げない' http://www.kijineko.[...] 주식회사키지네코 2010-02-28
[41] 웹사이트 CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') https://cwe.mitre.or[...] Mitre 2018-12-18
[42] 웹사이트 CWE-121: Stack-based Buffer Overflow https://cwe.mitre.or[...] Mitre 2018-12-18
[43] 웹사이트 CWE-122: Heap-based Buffer Overflow https://cwe.mitre.or[...] Mitre 2018-12-18
[44] 웹사이트 CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer https://cwe.mitre.or[...] Mitre 2018-12-18
[45] 웹사이트 CWE-787: Out-of-bounds Write https://cwe.mitre.or[...] Mitre 2018-12-18
[46] 웹사이트 CWE-193: Off-by-one Error https://cwe.mitre.or[...] Mitre 2018-12-18
[47] 웹사이트 CWE-131: Incorrect Calculation of Buffer Size https://cwe.mitre.or[...] Mitre 2018-12-18
[48] 웹사이트 CWE-680: Integer Overflow to Buffer Overflow https://cwe.mitre.or[...] Mitre 2018-12-18
[49] 웹사이트 The Exploitant - Security info and tutorials http://raykoid666.wo[...] 2009-11-29
[50] 간행물 STRIDE: Polymorphic Sled Detection through Instruction Sequence Analysis. http://dcs.ics.forth[...] IFIP International Information Security Conference
[51] 웹사이트 The Metasploit Opcode Database https://web.archive.[...] 2007-05-15
[52] 웹사이트 malloc(3)のメモリ管理構造 https://www.valinux.[...] VA Linux Systems Japan 2007-11-30
[53] 웹사이트 A Memory Allocator http://g.oswego.edu/[...] 2018-12-26
[54] 웹사이트 CWE-123: Write-what-where Condition https://cwe.mitre.or[...] Mitre 2018-12-26
[55] 웹사이트 JVNDB-2015-004721 Silicon Integrated Systems WindowsXP Display Manager における権限を取得される脆弱性 https://jvndb.jvn.jp[...] 2018-12-26
[56] 웹사이트 第14回: バッファオーバーフローとサーバ側のセキュリティ対策を考える https://thinkit.co.j[...] 2006-09-05
[57] 웹사이트 세큐어한 프로그래머 버퍼 오버플로에 대항하는 오늘 최대의 취약성을 방지한다 https://www.ibm.com/[...] IBM 2004-01-27
[58] 웹사이트 StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al. https://www.usenix.o[...] 2012-02-09
[59] 웹사이트 /GS (버퍼의 보안 체크) https://docs.microso[...] 2016-11-04
[60] 웹사이트 Libsafe - Free Software Directory http://directory.fsf[...] 2012-02-09
[61] 웹사이트 Windows XP Service Pack 2、Windows XP Tablet PC Edition 2005、및 Windows Server 2003의 데이터 실행 방지 (DEP) 기능의 상세 http://support.micro[...] 2012-02-17
[62] 웹사이트 Bypassing Windows Hardware-enforced Data Execution Prevention http://www.uninforme[...] 2007-05-20
[63] 웹사이트 PaX: Homepage of the PaX team http://pax.grsecurit[...] 2012-02-17
[64] 웹사이트 KernelTrap.Org https://archive.is/2[...] 2012-02-17
[65] 웹사이트 Openwall Linux kernel patch 2.4.34-ow1 http://linux.softped[...] 2012-02-17
[66] 웹사이트 BufferShield: Prevention of Buffer Overflow Exploitation for Windows http://www.sys-manag[...] 2012-02-17
[67] 웹사이트 NGSec Stack Defender https://web.archive.[...] 2012-02-17
[68] 웹사이트 NX비트 http://e-words.jp/w/[...] 2019-01-01
[69] 웹사이트 Linux kernel 3.14, Section 1.7. Kernel address space randomization https://kernelnewbie[...] 2014-03-30
[70] 웹사이트 iOS 6 Exploitation 280 Days Later https://www.slidesha[...] 2019-01-01
[71] 웹사이트 Hardening https://wiki.debian.[...] Devian 2019-01-01
[72] 웹사이트 Delphi Basics : $RangeChecks command http://www.delphibas[...] 2012-02-03
[73] 웹사이트 範囲チェック - RAD Studio https://docwiki.emba[...]
[74] 웹사이트 第10章 著名な脆弱性対策 バッファオーバーフロー: #2 ソースコード記述時の対策 https://www.ipa.go.j[...] 情報処理推進機構 2018-12-27
[75] 웹사이트 The Better String Library http://bstring.sf.ne[...] 2012-02-08
[76] 웹사이트 The Vstr Homepage http://www.and.org/v[...] 2012-02-08
[77] 웹사이트 The Erwin Homepage http://www.theiling.[...] 2012-02-08
[78] 웹사이트 第10章 著名な脆弱性対策 バッファオーバーフロー: #3 ソースコードの静的検査 https://www.ipa.go.j[...] 情報処理推進機構 2018-12-27
[79] conference Writing Metasploit Plugins: from vulnerability to exploit http://conference.hi[...]
[80] 웹사이트 CTFで学ぶ脆弱性(スタックバッファオーバーフロー編・その1) http://www.intellili[...] NTTデータ先端技術株式会社 2019-01-01
[81] 웹사이트 "A Tour of The Worm" by Donn Seeley, University of Utah http://world.std.com[...] 2007-06-03
[82] 웹사이트 Bugtraq security mailing list archive http://www.security-[...] 2007-06-03
[83] 웹사이트 Smashing the Stack for Fun and Profit http://www.phrack.or[...] 2007-06-03
[84] 웹사이트 eEye Digital Security http://research.eeye[...] 2007-06-03
[85] 웹사이트 Microsoft Technet Security Bulletin MS02-039 http://www.microsoft[...] 2007-06-03
[86] 웹사이트 Hacker breaks Xbox protection without mod-chip http://www.gamesindu[...] 2007-06-03
[87] 웹사이트 Routine Mappings(CRT) http://msdn.microsof[...]
[88] 서적 Safer C: Developing Software for High-integrity and Safety-critical Systems



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com